# 1.按照要求，完成VGG16以下处理
# ①数据处理
# 1)读取mnist数据集
from keras.datasets.fashion_mnist import load_data
from keras import Sequential, layers, activations, optimizers, losses, utils, Model

# 2)对数据进行维度转换、归一化等相关预处理
(x_train, y_train), (x_test, y_test) = load_data()
x_train = x_train.reshape(-1, 28, 28, 1) / 255
x_test = x_test.reshape(-1, 28, 28, 1) / 255


# ②设置VGG16模块（类），
# 1)声明一个Sequential包括网络结构中的所有功能层
# 2)根据下图VGG16网络结构构建模型类
# 3)卷积模型取前四组，且初始卷积核个数为16
# 4)每到一个新的卷积组通道数翻倍
# 5)最后三层全链接通道数分别为2048，512，10
# 6)Dropout层失活率设置为0.4
# 7)实现正向传播处理
class VGG16(Model):
    # ⑤通道数以16为起始，每组翻倍
    # ⑥每层卷积均采用3*3卷积核，步长均为1，padding均为same
    # ⑦每组卷积后接最大池化层，池化核2*2，步长为2
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.conv = Sequential([
            layers.Conv2D(filters=16, kernel_size=(3, 3), padding='same'),
            layers.Conv2D(filters=16, kernel_size=(3, 3), padding='same'),
            layers.MaxPooling2D(),
            layers.Conv2D(filters=32, kernel_size=(3, 3), padding='same'),
            layers.Conv2D(filters=32, kernel_size=(3, 3), padding='same'),
            layers.MaxPooling2D(),
            layers.Conv2D(filters=64, kernel_size=(3, 3), padding='same'),
            layers.Conv2D(filters=64, kernel_size=(3, 3), padding='same'),
            layers.Conv2D(filters=64, kernel_size=(3, 3), padding='same'),
            layers.MaxPooling2D(),
            layers.Conv2D(filters=128, kernel_size=(3, 3), padding='same'),
            layers.Conv2D(filters=128, kernel_size=(3, 3), padding='same'),
            layers.Conv2D(filters=128, kernel_size=(3, 3), padding='same'),
            layers.MaxPooling2D()
        ])
        self.flat = Sequential([layers.Flatten()])
        self.fc = Sequential([
            layers.Dense(units=64, activation=activations.relu),
            layers.Dropout(0.4),
            layers.Dense(units=64, activation=activations.relu),
            layers.Dropout(0.4),
            layers.Dense(units=10, activation=activations.softmax)
        ])

    def call(self, inputs, training=None, mask=None):
        out = self.conv(inputs)
        out = self.flat(out)
        out = self.fc(out)
        return out


# ③完成模型创建及训练
model = VGG16()
model.build(input_shape=(None, 28, 28, 1))
model.summary()
model.compile(optimizer=optimizers.Adam(), loss=losses.sparse_categorical_crossentropy, metrics='acc')
model.fit(x_train, y_train, batch_size=100, epochs=10)

# 1)输出模型检验后最终的损失值，准确率
model.evaluate(x_test, y_test)
